/****************************************************************************** 

  文 件 名   : mini_telnet.c
  版 本 号   : V1.0
  作    者   : moomin
  生成日期   : 2022年3月9日
  最近修改   :
  功能描述   : mini telnet  由mini shell + NVT协议解析 + telnet登录流程串联起来，故两者相同功能部分源码大致相同
				但是经过xshell实际调试，对部分与shell有较大体验差异的地方做了针对性修改
  函数列表   :
  修改历史   :
  1.日    期   : 2022年3月9日
    作    者   : moomin
    修改内容   : 创建文件
******************************************************************************/
#include "mini_telnet.h"
#define TELNET_DBG      //logblue
/*shell queue*/

#define TRUE 	1
#define FALSE 	0
#define ENABLE	1
#define DISABLE	0

//queue init.
#define QUEUE_INIT(q)       queue_init((uint16_t *) &(q.Front),(uint16_t *) &(q.Rear),(uint8_t *) &(q.PBase[0]),(uint16_t) (sizeof(q.PBase)))
//queue full?
#define QUEUE_FULL(q)       queue_full((uint16_t *) &(q.Front),(uint16_t *) &(q.Rear),(uint8_t *) &(q.PBase[0]),(uint16_t) (sizeof(q.PBase)))
//queue empty?
#define QUEUE_EMPTY(q)      queue_empty((uint16_t *) &(q.Front),(uint16_t *) &(q.Rear),(uint8_t *) &(q.PBase[0]),(uint16_t) (sizeof(q.PBase)))
//put pdata in queue
#define QUEUE_IN(q, pdata)  queue_in((uint16_t *) &(q.Front),(uint16_t *) &(q.Rear),(uint8_t *) &(q.PBase[0]),(uint16_t) (sizeof(q.PBase)),(uint8_t *) &pdata)
//get byte from queue
#define QUEUE_OUT(q, pdata) queue_out((uint16_t *) &(q.Front),(uint16_t *) &(q.Rear),(uint8_t *) &(q.PBase[0]),(uint16_t) (sizeof(q.PBase)),(uint8_t *) &pdata)
 
 typedef struct queue2048 {
	uint16_t    Front;
	uint16_t    Rear;
	uint8_t     PBase[2048 + 1];
} QUEUE2048_S;

uint8_t queue_init(uint16_t *Front, uint16_t *Rear, uint8_t *PBase, uint16_t Len)
{
    uint16_t index;

    for(index = 0; index < Len; index++) {
        PBase[index] = NULL;
    }

    *Front = *Rear = 0;
    return TRUE;
}
uint8_t queue_full(uint16_t *Front, uint16_t *Rear, uint8_t *PBase, uint16_t Len)
{
    if((((*Rear) + 1) % Len) == *Front) {
        return TRUE;
    } else {
        return FALSE;
    }
}
uint8_t queue_empty(uint16_t *Front, uint16_t *Rear, uint8_t *PBase, uint16_t Len)
{
    if(*Front == *Rear) {
        return TRUE;
    } else {
        return FALSE;
    }
}
uint8_t queue_in(uint16_t *Front, uint16_t *Rear, uint8_t *PBase, uint16_t Len, uint8_t *PData)
{
    if(queue_full(Front, Rear, PBase, Len)) {
        return FALSE;
    }

    PBase[*Rear] = *PData;
    *Rear = ((*Rear) + 1) % Len;

    return TRUE;
}
uint8_t queue_out(uint16_t *Front, uint16_t *Rear, uint8_t *PBase, uint16_t Len, uint8_t *PData)
{
    if(queue_empty(Front, Rear, PBase, Len)) {
        return FALSE;
    }

    *PData = PBase[*Front];
    *Front = ((*Front) + 1) % Len;

    return TRUE;
}

/* terminal display-----------------------------------------------------BEGIN */

/*
	@terminal setting commands:
		\033[0m     reset all
		\033[1m     set high brightness
		\03[4m      underline
		\033[5m     flash
		\033[7m     reverse display
		\033[8m     blanking
		\033[30m    --  \033[37m  set font color
		\033[40m    --  \033[47m  set background color
		\033[nA     cursor up up n lines
		\033[nB     cursor move up n lines
		\033[nC     cursor move right n lines
		\033[nD     cursor left up n lines
		\033[y;xH   set cursor position
		\033[2J     clear all display
		\033[K      clear line
		\033[s      save cursor position
		\033[u      restore cursor position
		\033[?25l   cursor invisible
		\33[?25h    cursor visible


	@background color: 40--49           @font color: 30--39
		40: BLACK                           30: black
		41: RED                             31: red
		42: GREEN                           32: green
		43: YELLOW                          33: yellow
		44: BLUE                            34: blue
		45: PURPLE                          35: purple
		46: CYAN                            36: deep green
		47: WHITE                           37: white
*/

/* font color */
#define TERMINAL_FONT_BLACK()       sh_printf("\033[1;30m")
#define TERMINAL_FONT_L_RED()       sh_printf("\033[0;31m")    /* light red */
#define TERMINAL_FONT_RED()         sh_printf("\033[1;31m")    /* red */
#define TERMINAL_FONT_GREEN()       sh_printf("\033[1;32m")
#define TERMINAL_FONT_YELLOW()      sh_printf("\033[1;33m")
#define TERMINAL_FONT_BLUE()        sh_printf("\033[1;34m")
#define TERMINAL_FONT_PURPLE()      sh_printf("\033[1;35m")
#define TERMINAL_FONT_CYAN()        sh_printf("\033[1;36m")
#define TERMINAL_FONT_WHITE()       sh_printf("\033[1;37m")

/* background color */
#define TERMINAL_BACK_BLACK()       sh_printf("\033[1;40m")
#define TERMINAL_BACK_L_RED()       sh_printf("\033[0;41m")    /* light red */
#define TERMINAL_BACK_RED()         sh_printf("\033[1;41m")    /* red */
#define TERMINAL_BACK_GREEN()       sh_printf("\033[1;42m")
#define TERMINAL_BACK_YELLOW()      sh_printf("\033[1;43m")
#define TERMINAL_BACK_BLUE()        sh_printf("\033[1;44m")
#define TERMINAL_BACK_PURPLE()      sh_printf("\033[1;45m")
#define TERMINAL_BACK_CYAN()        sh_printf("\033[1;46m")
#define TERMINAL_BACK_WHITE()       sh_printf("\033[1;47m")

/* terminal clear end */
#define TERMINAL_CLEAR_END()        sh_printf("\033[K")

/* terminal clear all */
#define TERMINAL_DISPLAY_CLEAR()    sh_printf("\033[2J")

/* cursor move up */
#define TERMINAL_MOVE_UP(x)         sh_printf("\033[%dA", (x))

/* cursor move down */
#define TERMINAL_MOVE_DOWN(x)       sh_printf("\033[%dB", (x))

/* cursor move left */
#define TERMINAL_MOVE_LEFT(y)       sh_printf("\033[%dD", (y))

/* cursor move right */
#define TERMINAL_MOVE_RIGHT(y)      sh_printf("\033[%dC",(y))

/* cursor move to */
#define TERMINAL_MOVE_TO(x, y)      sh_printf("\033[%d;%dH", (x), (y))

/* cursor reset */
#define TERMINAL_RESET_CURSOR()     sh_printf("\033[H")

/* cursor invisible */
#define TERMINAL_HIDE_CURSOR()      sh_printf("\033[?25l")

/* cursor visible */
#define TERMINAL_SHOW_CURSOR()      sh_printf("\033[?25h")

/* reverse display */
#define TERMINAL_HIGH_LIGHT()       sh_printf("\033[7m")
#define TERMINAL_UN_HIGH_LIGHT()    sh_printf("\033[27m")

/* terminal display-------------------------------------------------------END */


/*mini shell*/
#define KEY_UP              "\x1b\x5b\x41"  /* [up] key: 0x1b 0x5b 0x41 */
#define KEY_DOWN            "\x1b\x5b\x42"  /* [down] key: 0x1b 0x5b 0x42 */
#define KEY_RIGHT           "\x1b\x5b\x43"  /* [right] key: 0x1b 0x5b 0x43 */
#define KEY_LEFT            "\x1b\x5b\x44"  /* [left] key: 0x1b 0x5b 0x44 */
#define KEY_ENTER           '\r'            /* [enter] key */
#define KEY_LINE_FEED       '\n'            /* [alt + enter] key */
#define KEY_BACKSPACE       '\b'            /* [backspace] key */
#define KEY_TAB       		0x09            /* [tab] key */

#define RX_BUFF_TYPE        QUEUE2048_S

 typedef struct {
#define HANDLE_LEN 128

	uint8_t buff[HANDLE_LEN];
	uint8_t len;
} HANDLE_TYPE_S;


#define SHELL_HISTORY         	TRUE                    /* 开启历史记录 */
#define HISTORY_MAX         	10                      /* 最大支持10条历史记录d */

static RX_BUFF_TYPE cli_rx_buff; 
static uint8_t s_mini_shell_init_flag = 0;
const COMMAND_S *pshell_cmd;	/*命令行注册信息*/
uint16_t g_u16shell_cmd_num;    /*注册命令个数*/
static func_send func_sh_putc;

#define CMD_BUFFER_LEN 4096
static char s_acstring[CMD_BUFFER_LEN + 1];
void sh_printf(const char *fmt, ...)
{
    uint16_t u16len;
    if(!s_mini_shell_init_flag)
        return;
    va_list arg_ptr; 
    va_start(arg_ptr, fmt); 
    u16len = vsnprintf(s_acstring, CMD_BUFFER_LEN - 1, fmt, arg_ptr); 
    if(NULL != func_sh_putc)
    {
        func_sh_putc(s_acstring,u16len);
    }
    va_end(arg_ptr);
}
#if SHELL_HISTORY

 typedef struct {
	char cmd[HISTORY_MAX][HANDLE_LEN];
	uint8_t count;
	uint8_t latest;
	uint8_t show;
} HISTORY_S;

static HISTORY_S history;

static void sh_history_add(char *buff)
{
	uint16_t len;
	uint8_t index = history.latest;

	if (NULL == buff)
		return;

	len = strlen((const char *)buff);
	if (len >= HANDLE_LEN)
		return;  

	if (0 != index) {
		index--;
	} else {
		index = HISTORY_MAX - 1;
	}

	if (0 != memcmp(history.cmd[index], buff, len)) {
		memset((void *)history.cmd[history.latest], 0x00, HANDLE_LEN);
		memcpy((void *)history.cmd[history.latest], (const void *)buff, len);
		if (history.count < HISTORY_MAX) {
			history.count++;
		}

		history.latest++;
		if (history.latest >= HISTORY_MAX) {
			history.latest = 0;
		}
	}

	history.show = 0;
}

static uint8_t sh_history_show(uint8_t mode, char **p_history)
{
	uint8_t err = TRUE;
	uint8_t num;
	uint8_t index;

	if (0 == history.count)
		return err;

	if (TRUE == mode) {
		if (history.show < history.count) {
			history.show++;
		}
	} else {
		if (1 < history.show) {
			history.show--;
		}
	}

	num = history.show;
	index = history.latest;
	while (num) {
		if (0 != index) {
			index--;
		} else {
			index = HISTORY_MAX - 1;
		}
		num--;
	}

	err = FALSE;
	*p_history = history.cmd[index];

	return err;
}

#endif  

static void mini_shell_init(const COMMAND_S *pcmd,uint16_t u16cmd_num,func_send fputc)
{
	uint8_t i;
    if(NULL == pcmd)
    {
        return;
    }
    if(NULL == fputc)
    {
        return;
    }
	QUEUE_INIT(cli_rx_buff);
#if SHELL_HISTORY
	memset((uint8_t *)&history, 0, sizeof(history));
#endif 
	memset((uint8_t *)&cli_rx_buff, 0, sizeof(RX_BUFF_TYPE));
    g_u16shell_cmd_num = u16cmd_num;
    pshell_cmd = pcmd; 
    func_sh_putc = fputc;
	for (i = 0; i < g_u16shell_cmd_num; i++) {
		if (NULL != pshell_cmd[i].pInit) {
			if (FALSE == pshell_cmd[i].pInit()) {
				sh_printf("\r\n-> FUN[%d] INIT WRONG\r\n", i);
			}
		}
	}
	s_mini_shell_init_flag = 1; 
}
static void mini_shell_deinit(void)
{
	uint8_t i;
#if SHELL_HISTORY
	memset((uint8_t *)&history, 0, sizeof(history));
#endif 
	memset((uint8_t *)&cli_rx_buff, 0, sizeof(RX_BUFF_TYPE));
    g_u16shell_cmd_num = 0;
    pshell_cmd = NULL; 
    func_sh_putc = NULL;
	s_mini_shell_init_flag = 0; 
}
/*
    src 源字符串的首地址(buf的地址)
    separator 指定的分割字符
    dest 接收子字符串的数组
    num 分割后子字符串的个数
*/
static void split(char *src,const char *separator,char **dest,int *num) {
	/*
		src 源字符串的首地址(buf的地址)
		separator 指定的分割字符
		dest 接收子字符串的数组
		num 分割后子字符串的个数
	*/
     char *pNext;
     int count = 0;
     if (src == NULL || strlen(src) == 0) //如果传入的地址为空或长度为0，直接终止
        return;
     if (separator == NULL || strlen(separator) == 0) //如未指定分割的字符串，直接终止
        return;
     pNext = (char *)strtok(src,separator); //必须使用(char *)进行强制类型转换(虽然不写有的编译器中不会出现指针错误)
     while(pNext != NULL) {
          *dest++ = pNext;
          ++count;
         pNext = (char *)strtok(NULL,separator);  //必须使用(char *)进行强制类型转换
    }
    *num = count;
}


/*字符分割 */
const char SPLIT_C = ' ';
const char USER[] = "[root]$";
static void shell_rx_handle(RX_BUFF_TYPE *rx_buff)
{
	static HANDLE_TYPE_S Handle = {.len = 0};
	uint8_t i = Handle.len;
	//uint8_t ParaLen;
	uint8_t *ParaAddr;
	uint8_t cmd_match = FALSE;
	uint8_t j = 0;
    char *param[32];
    int num = 0;

	while (1) {
		if (Handle.len < HANDLE_LEN) {
			if (TRUE == QUEUE_OUT((*rx_buff), Handle.buff[Handle.len])) {
				if (KEY_BACKSPACE == Handle.buff[Handle.len]) {
					if (0 < Handle.len) {
                        TERMINAL_MOVE_LEFT(1);
						TERMINAL_CLEAR_END();
						Handle.len -= 1;
					}
				} else {
					Handle.len++;
				}
			} else {
#if SHELL_HISTORY
				uint8_t key = 0;
				uint8_t err = 0xff;
				char *p_hist_cmd = 0;

				if (Handle.len > 2) {
					if (0 != strstr((const char *)Handle.buff, KEY_UP)) {
						key = 1;
						TERMINAL_MOVE_LEFT(Handle.len+7);
						sh_printf("%s",USER);
						TERMINAL_CLEAR_END();
						err = sh_history_show(TRUE, &p_hist_cmd);
					} else if (0 != strstr((const char *)Handle.buff, KEY_DOWN)) {
						key = 2;
						TERMINAL_MOVE_LEFT(Handle.len+7);
						sh_printf("%s",USER);
						TERMINAL_CLEAR_END();
						err = sh_history_show(FALSE, &p_hist_cmd);
					} else if (0 != strstr((const char *)Handle.buff, KEY_RIGHT)) {
						TERMINAL_MOVE_LEFT(Handle.len+7);
						sh_printf("%s",USER);
						TERMINAL_CLEAR_END();
						key = 3;
					} else if (0 != strstr((const char *)Handle.buff, KEY_LEFT)) {
						TERMINAL_MOVE_LEFT(Handle.len+7);
						sh_printf("%s",USER);
						TERMINAL_CLEAR_END();
						key = 4;
					}
					if (0 != key) {
						if (FALSE == err) {
							memset(&Handle, 0x00, sizeof(Handle));
							memcpy(Handle.buff, p_hist_cmd, strlen(p_hist_cmd));
							Handle.len = strlen(p_hist_cmd);
							Handle.buff[Handle.len] = '\0';
							sh_printf("%s", Handle.buff);  
						} else if ((TRUE == err) || (0 != key)) {
							TERMINAL_CLEAR_END();
							memset(&Handle, 0x00, sizeof(Handle));
						}
					}
				}
				if( KEY_TAB == Handle.buff[Handle.len - 1])
				{
					sh_printf("KEY_TAB \r\n");
				}
				else if ((0 == key) && (i < Handle.len)) {
#endif 
					for (; i < Handle.len; i++) {
						if(NULL != func_sh_putc)
							func_sh_putc(&Handle.buff[i],1);
					}
#if SHELL_HISTORY
				}
#endif 
				break;
			}
		} else {
			break;
		}
	}
	if ((1 == Handle.len) && ((KEY_ENTER == Handle.buff[Handle.len - 1]) || (KEY_LINE_FEED == Handle.buff[Handle.len - 1]))) {
		if(KEY_LINE_FEED != Handle.buff[Handle.len - 1])
		{
			sh_printf("\n%s",USER);
		}
		Handle.len = 0;
	} else if (1 < Handle.len) {
		if (KEY_ENTER == Handle.buff[Handle.len - 1] || KEY_LINE_FEED == Handle.buff[Handle.len - 1]) {
			Handle.buff[Handle.len - 1] = '\0';
			for (i = 0; i < g_u16shell_cmd_num; i++) {
				if (0 == strncmp((const char *)Handle.buff,
				                 (void *)pshell_cmd[i].pCmd,
				                 strlen(pshell_cmd[i].pCmd))) {
					cmd_match = TRUE;
					ParaAddr = &Handle.buff[0]; 
                    split((char *)ParaAddr,&SPLIT_C,param,&num);
					if (NULL != pshell_cmd[i].pFun) {
						sh_printf("\r\n");
						if (pshell_cmd[i].pFun(num, param)) {
							sh_printf("\r\n-> OK\r\n");
#if SHELL_HISTORY
							sh_history_add((char *)Handle.buff);
#endif 
						} else {
							sh_printf("\r\n-> PARA. ERR\r\n");
							sh_printf(pshell_cmd[i].pHelp);
						}
					} else {
						sh_printf("\r\n-> FUNC. ERR\r\n");
					}
				}
			}
			if (FALSE == cmd_match) {
				sh_printf("\r\n-> CMD ERR, try: help\r\n\r\n");
			}
			Handle.len = 0;
		}
	}
	if (Handle.len >= HANDLE_LEN) {
		Handle.len = 0;
	}
}

static void mini_shell_run(void)
{
	if(!s_mini_shell_init_flag)
		return;
	if(NULL == pshell_cmd )
		return;
	shell_rx_handle(&cli_rx_buff);
}

/*
    接收数据
*/
static void get_c(uint8_t u8res)
{
	if(!s_mini_shell_init_flag)
		return;
	QUEUE_IN(cli_rx_buff, u8res);
}



/*
    telnet服务端在响应客户机接入后，需要与客户机进行协商，这里我们尝试进行最简单的连接，仅需要掌握一些常用命令。
    see -->  https://forum.huawei.com/enterprise/zh/thread-357453-1-1.html
        发送方发出请求	    含义	                                接受方应答			
		                                         WILL	    WONT	        DO          DONT
        WILL	        发送方想激活选项	        －	        －	        接收方同意	  接收方不同意
        WONT	        发送方想禁止选项	        －	        －  	       －	     接收方必须同意(1)
        DO	            发送方想让接收方激活选项	接收方同意	接收方不同意	    －	        －
        DONT	        发送方想让接收方禁止选项	－	      接收方必须同意(1)	   －	       －

    ...
    TELNET的常用命令（具体的见TCP/IP详解P303)：
    SE    240(F0)     子选项结束
    SB    250(FA)     子选项开始
    IAC   255(FF)     选项协商的第一个字节
    WILL  251(FB)     发送方激活选项(接收方同意激活选项)
    DO    253(FD)     接收方同意（发送方想让接收方激活选项）
    WONT  252(FC)     接收方不同意
    DONT  254(FE)     接受方回应WONT

    TELNET的常用选项代码（具体的见TCP/IP详解P304)：
    1(0x01)    回显(echo)
    3(0x03)    抑制继续进行(传送一次一个字符方式可以选择这个选项)
    24(0x18)   终端类型
    31(0x1F)   窗口大小
    32(0x20)   终端速率
    33(0x21)   远程流量控制
    34(0x22)   行方式
    36(0x24)   环境变量
*/

enum TELNET_CMD_E{
	TELNET_CMD_SE       = 0xF0,
    TELNET_CMD_SB       = 0xFA,
    TELNET_CMD_WILL     = 0xFB,
    TELNET_CMD_WONT     = 0xFC,
    TELNET_CMD_DO       = 0xFD,
    TELNET_CMD_DONT     = 0xFE,
    TELNET_CMD_IAC      = 0xFF,
	/*
        ...
    */
};

enum TELNET_OPT_E{
	TELNET_OPT_ECHO                 = 0x01,
	TELNET_OPT_BYTE                 = 0x03,
	TELNET_OPT_TERMINAL_TYPE        = 0x18,
	TELNET_OPT_WINDOW               = 0x1F,
	TELNET_OPT_TERMINAL_RATE        = 0x20,
	TELNET_OPT_TERMINAL_FLOW        = 0x21,
    /*
        ...
    */
};

enum TELNET_LOGIN_E{
	TELNET_LOGIN_WELCOME = 0,
	TELNET_LOGIN_USER ,
	TELNET_LOGIN_PASSWORD,
	TELNET_LOGIN_SUCCES,
};
typedef struct {
	char admin_username[32];
	char admin_password[32];

	char username[32];
	char password[32];
	int8_t name_len;
	int8_t pw_len;

} USER_INFO_T;

/* NVT-----------------------------------------------------BEGIN */
#define MAX_CALLBACK_FUN		32

typedef int (*fn_analy_frame)(uint8_t *p_context, uint8_t len);

typedef struct
{
  uint8_t cmd;
  fn_analy_frame call_back;
}cmd_fun_t;

typedef struct
{
    uint8_t index;
    cmd_fun_t CMD_FUN[MAX_CALLBACK_FUN];
}cmd_index_t;

static cmd_index_t port_cmd_fun = {0};  
static enum TELNET_LOGIN_E telnet_conn_sta = TELNET_LOGIN_USER;
static USER_INFO_T telnet_user_info = {0};


static uint8_t nvt_cmd_add(uint8_t cmd, fn_analy_frame call_back)
{
	uint8_t i;
    
	if(port_cmd_fun.index >= MAX_CALLBACK_FUN) 
		return 0;
   
	for(i=0; i<port_cmd_fun.index; i++)
	{
		if(port_cmd_fun.CMD_FUN[i].cmd == cmd)
			return 0;      
	}  
	port_cmd_fun.CMD_FUN[port_cmd_fun.index].cmd = cmd;
	port_cmd_fun.CMD_FUN[port_cmd_fun.index].call_back = call_back;
	port_cmd_fun.index++;
	return 1;
}
static void nvt_cmd_fun_init(void)
{
	port_cmd_fun.index = 0;
	for(int j=0; j< MAX_CALLBACK_FUN; j++)
	{
		port_cmd_fun.CMD_FUN[j].cmd = 0;
		port_cmd_fun.CMD_FUN[j].call_back = NULL;
	}
}
static int nvt_com_cmd_fun_scan(uint8_t cmd,uint8_t *buffer,uint16_t size)
{
    for(int i=0; i<port_cmd_fun.index; i++)
    {
        if(cmd == port_cmd_fun.CMD_FUN[i].cmd)
        {
            if(port_cmd_fun.CMD_FUN[i].call_back != NULL)
            {
                return port_cmd_fun.CMD_FUN[i].call_back(buffer,size);
            }
        }
    }
    return -1;
}
static int nvt_frame_decode(uint8_t *p_context, uint8_t len)
{
    TELNET_DBG("rx NVT data :%d",len);

    return 0;
}
/* NVT-----------------------------------------------------END */



/*
    telnet 客户机接入主动发起协商
*/
int telnet_client_conn_callback(const COMMAND_S *pcmd,char* name,char* password,uint16_t u16cmd_num,func_send fsend)
{
    int ret = -1;
    uint8_t buff[] = {TELNET_CMD_IAC,TELNET_CMD_DO,TELNET_OPT_TERMINAL_TYPE,\
                        TELNET_CMD_IAC,TELNET_CMD_WILL,TELNET_OPT_ECHO,\
                        TELNET_CMD_IAC,TELNET_CMD_WILL,TELNET_OPT_BYTE};

    if(NULL == fsend)
        return ret;

    nvt_cmd_fun_init();
    nvt_cmd_add(TELNET_CMD_IAC,nvt_frame_decode);
    ret = fsend(buff,sizeof(buff));
    TELNET_DBG("telnet_client_conn_callback. set NVT cmd. ret:%d",ret);
    mini_shell_init(pcmd,u16cmd_num,fsend);
	telnet_conn_sta = TELNET_LOGIN_WELCOME;
	memset((uint8_t *)&telnet_user_info,0x00,sizeof(telnet_user_info));
	strncpy(telnet_user_info.admin_username,name,sizeof(telnet_user_info.admin_username));
	strncpy(telnet_user_info.admin_password,password,sizeof(telnet_user_info.admin_password));
    return ret;
}

/*
    telnet 
*/
int telnet_client_disconn_callback(void)
{
    mini_shell_deinit();
	telnet_conn_sta = TELNET_LOGIN_WELCOME;
	memset((uint8_t *)&telnet_user_info,0x00,sizeof(telnet_user_info));
	TELNET_DBG("telnet_client_disconn_callback. deinit.");
}

/*
    telnet  需要先对接受数据进行NVT解析，登录流程管理
*/
int telnet_client_recv_callback(uint8_t *pbuff,uint16_t len)
{
	
    if(NULL == pbuff)
        return -1;
	/*
		telnet 欢迎语
		...
	*/
    if(-1 != nvt_com_cmd_fun_scan(pbuff[0],pbuff,len))
    {
		if(TELNET_LOGIN_WELCOME == telnet_conn_sta)
		{
			sh_printf("mini telnet v1.0.0 \r\n");
			sh_printf("author : moomin.\r\n");
			sh_printf("login:");
			telnet_conn_sta = TELNET_LOGIN_USER;
			return;
		}
        return 0;
    }
	/*
		登录流程-- 输入用户名
		...
	*/
	if(TELNET_LOGIN_USER == telnet_conn_sta)
	{
		if(telnet_user_info.name_len >= sizeof(telnet_user_info.username))
		{
			sh_printf("ERROR.\r\n");
			return -1;
		}
		for(int i = 0;i < len; i++)
		{
			if(pbuff[i] != KEY_ENTER)
			{
				telnet_user_info.username[telnet_user_info.name_len] = pbuff[i];
				telnet_user_info.name_len++;
				sh_printf("%c",pbuff[i]);
			}
			else{
				telnet_conn_sta = TELNET_LOGIN_PASSWORD;
				sh_printf("\r\nPassword:");
			}
		}
		return 0;
	}
	if(TELNET_LOGIN_PASSWORD == telnet_conn_sta)
	{
		if(telnet_user_info.pw_len >= sizeof(telnet_user_info.password))
		{
			sh_printf("ERROR.\r\n");
			return;
		}
		for(int i = 0;i < len; i++)
		{
			if(pbuff[i] != KEY_ENTER)
			{
				telnet_user_info.password[telnet_user_info.pw_len] = pbuff[i];
				telnet_user_info.pw_len++;
			}
			else{
				/*
					用户密码输入完成，进行信息比对
				*/
				if(strcmp(telnet_user_info.password,telnet_user_info.admin_password) == 0 && \
					strcmp(telnet_user_info.admin_username,telnet_user_info.username) == 0)
				{
					telnet_conn_sta = TELNET_LOGIN_SUCCES;
					sh_printf("\r\nwelcome to use mini telnet. \r\n");
				}
				else{
					telnet_conn_sta = TELNET_LOGIN_USER;
					memset((uint8_t *)&telnet_user_info.password,0x00,sizeof(telnet_user_info.password));
					memset((uint8_t *)&telnet_user_info.username,0x00,sizeof(telnet_user_info.username));
					telnet_user_info.pw_len = 0;
					telnet_user_info.name_len = 0;
					sh_printf("\r\npassword fail. \r\n");
					sh_printf("please input user name:");
				}
			}
		}
		return 0;
	}

    for(int i = 0 ; i < len ; i++)
    {
        get_c(pbuff[i]);
    }
    mini_shell_run();
    return 0;
}